﻿using System;
using System.Collections;
using System.Collections.Generic;


namespace DailyFx
{
	public class BarSeries : ITimeSeries<Bar>
	{
			
		// c'tor 
		public BarSeries ( Int32 n)
		{
			this._items = new List<Bar>( n);

			this.FieldMask	= FieldMask.Unknown;
			this.Period = Period.Daily;
		}


		/// <summary>
		///		Contains all bar/stick items .
		/// </summary>
		private System.Collections.Generic.List<Bar> _items ;


		//
		public Boolean HasTick ( DateTime tick )
		{
			return GetRadix( tick) >= 0;
		}
		

		public DateTime GetTick ( Int32 radix )
		{
			return this._items[ radix].Tick ;
		}

		/// <summary>
		///		performs a binary search, upon the time line . 
		/// </summary>
		public Int32 GetRadix ( DateTime tick )
		{
			int radix; // 
			int l = 0;
			int r = this.Length;

			while (r > l)
			{
				int m = l + (r - l) / 2;
				var item = this._items[m];

				if ( item.Tick < tick )
					l = m + 1;
				else if ( item.Tick > tick )
					r = m;
				// As a matter of fact, our Bars' date won't be duplicated, 
				// so this rounting is not neccessarily have to be performed ,
				else
				{
					int lFound = l, rFound = r, found = m;
					// Find the start of the run.
					l = lFound; r = found;
					while (r > l)
					{
						m = l + (r - l) / 2;
						item = this._items[m];

						if ( item.Tick < tick )
							l = m + 1;
						else
							r = m;
					}
					// System.Diagnostics.Debug.Assert(l == r, "xxxx");
					radix = l;

					// Find the end of the run.
					l = found; r = rFound;
					while (r > l)
					{
						m = l + (r - l) / 2;
						item = this._items[m];

						if ( item.Tick <= tick )
							l = m + 1;
						else
							r = m;
					}
					//  System.Diagnostics.Debug.Assert(l == r, "xxxx");
					int number = l - radix;
					return radix;
				}
			}
			// System.Diagnostics.Debug.Assert(l == r);
			// We did not find the Bar at 'day'. 
			return ~l; // ??

		}

		//
		public Int32 Add ( Bar item )
		{
			this._items.Add ( item);
			return this.Length;
		}

		public Bar Pop ( int radix )
		{
			Bar i = this._items[ radix];
			this._items.RemoveAt( radix);

			return i;
		}

		public Bar Pop ( DateTime tick )
		{
			int index = GetRadix( tick ); // todo: what if not exsits ?
			return this.Pop( index);
		}

		
		/// <summary>
		///		Inserts a new point into the time series. 
		///		if the datetime already has a value, then it either replaces it or throws an exception .
		/// </summary>
		public Boolean Pin2 ( Bar item, Boolean overWriteIfExists )
		{
			// assert ( when == item.When )
			// todo: modify item's time-stamp, if not identical .

			if (Length == 0)
			{
				this._items.Add(item);
				return true;
			}

			int radix = GetRadix( item.Tick );
			if (radix >= 0) // got a match
			{
				if (overWriteIfExists)
				{
					this._items[radix] = item;
					return true;
				}
				//throw new ApplicationException("Cannot pin over an existing point.");
				return false;
			}

			int i0 = ~radix - 1; // the index of the time before the searched time
			if (i0 == -1) // it goes at the front
			{
				this._items.Insert(0, item);
			}
			else if (i0 == Length - 1) // it goes at the end
				this._items.Add(item);
			else // it goes in somewhere
			{
				this._items.Insert(i0 + 1, item);
			}

			return true;
		}

		//
		public Bar this [ int radix ]
		{
			get
			{
				return this._items[radix];
			}
			set
			{
				this._items[radix] = value;
			}
		}

		public Bar this [ DateTime tick ]
		{
			get
			{
				int radix = GetRadix( tick);

				if (radix >= 0 ) // found !!!
				{
					return _items [radix];
				}
				else
				{
					// todo: !!
					// int i = ~index;
					//return sticks[ i];

					throw new ArgumentException("Given time stamp not found in the series .");
				}
			}

			set
			{
				int index = GetRadix( tick );
				this._items [index] = value;
			}

		}

		public Decimal this [ Int32 field, Int32 radix ]
		{
			get
			{
				return _items[radix][field];
			}
			set
			{
				throw new NotImplementedException("");
				// sticks[index][field] = value;
			}
		}


		public Decimal this [ DateTime tick, Int32 field ]
		{
			get
			{
				int index = GetRadix( tick );
				return this[ index, field];
			}
			set
			{
				int index = GetRadix( tick );
				this[index, field] = value;
			}
		}


		/// <summary>
		///		Gets or sets <see cref="Bar.FieldMask"/> .
		/// </summary>
		public FieldMask FieldMask
		{
			get;
			set;
		}

		/// <summary>
		///		Gets or sets the <see cref="Bar.Period"/> .
		/// </summary>
		public Period Period
		{
			get;
			set;
		}


		public int Rank
		{
			// todo: it's depends on Bar Fields' Mask
			get { return 5; }
		}

		/// <summary>
		///     How many bar items ?
		/// </summary>
		public int Length
		{
			get { return _items.Count; }
		}


		/// <summary>
		///     Returns implicitly raw item list .
		/// </summary>
		public IReadOnlyList<Bar> AsReadOnly ()
		{
			return this._items;
		}


		public IEnumerator<Bar> GetEnumerator ()
		{
			return this._items .GetEnumerator();
		}

		// Hidden .
		IEnumerator IEnumerable.GetEnumerator ()
		{
			return this._items.GetEnumerator();
		}

	}

}
